﻿//
// Copyright (c) 2010
// by Chris Paterra
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.UI.WebControls;
using System.Web.UI;
using System;
using DotNetNuke.ContentLibrary.Components.Common;
using DotNetNuke.ContentLibrary.Components.Controllers;
using DotNetNuke.ContentLibrary.Components.Entities;
using DotNetNuke.ContentLibrary.Components.Templates;
using Telerik.Web.UI;

namespace DotNetNuke.ContentLibrary.Components.WebControls {

	/// <summary>
	/// The reviews control contains a review input form and a list view of reviews.
	/// </summary>
	[DefaultEvent("Submit"), DefaultProperty("Width"), Themeable(false)]
	[ToolboxData("<{0}:Reviews runat=\"server\"> </{0}:Reviews>")]
	public class Reviews : ControlBase
	{
	
		#region Private Members

		private TextBox _title;
		private DnnRating _overallRating;
		private TextBox _proComment;
		private TextBox _conComment;
		private TextBox _comment;
		private RequiredFieldValidator _valComment;
		private LinkButton _cmdSubmit;
		private DnnListView _listView;

		private static readonly object EventSubmitKey = new object();
		private string _buttonCssClass = "CommandButton";
		private string _labelCssClass = "SubHead";
		private string _textBoxCssClass = "NormalTextBox";
		private int _pageSize = 5;
// ReSharper disable InconsistentNaming
		private const bool _requireAuthentication = true;
		private const bool _allowUserMultipleReviews = false;
// ReSharper restore InconsistentNaming
		private string _validatorCssClass = "NormalRed";
		
		/// <summary>
		/// This takes the supplied Child Name and returns a valid ID from the data store (via other methods). If the item does not exist, it is created as needed. 
		/// </summary>
		private int ChildTypeID() {
			if (ChildType == "") {
				return 0;
			}

			// query child types
			var cntController = new ContentLibraryController();
			var objChildType = (from t in cntController.GetChildTypes() where (t.ChildType == ChildType) select t).SingleOrDefault();
			if (objChildType != null) {
				return objChildType.ChildTypeID;
			}

			// create the child type
			var objType = new ChildTypeInfo { ChildType = ChildType, CreatedOnDate = DateTime.Now };
			return cntController.AddChildType(objType);
		}

		/// <summary>
		/// This is the review you are editing, should only be set when attempting to edit a review. 
		/// </summary>
		private int ReviewID {
			get {
				var reviewID = 0;
				if (ViewState["ReviewID"] != null) {
					reviewID = Convert.ToInt32(ViewState["ReviewID"]);
				}
				return reviewID;
			}
			set { ViewState["ReviewID"] = value; }
		}

		/// <summary>
		/// Determines if the contorl is in 'edit' mode. 
		/// </summary>
		private bool IsEditMode {
			get {
				var isEditMode = false;
				if (ViewState["IsEditMode"] != null) {
					isEditMode = Convert.ToBoolean(ViewState["IsEditMode"]);
				}
				return isEditMode;
			}
			set { ViewState["IsEditMode"] = value; }
		}

		/// <summary>
		/// Used within the control to manage the current page index of the grid.
		/// </summary>
		private int PageIndex {
			get {
				var pageIndex = 0;
				if (ViewState["PageIndex"] != null) {
					pageIndex = Convert.ToInt32(ViewState["PageIndex"]);
				}
				return pageIndex;
			}
			set { ViewState["PageIndex"] = value; }
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// This Css class is applied to the "Save" link button. 
		/// </summary>
		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("CommandButton"), 
		Description("The Css class applied to the submit link button."), Localizable(false)]
		public string ButtonCssClass {
			get { return _buttonCssClass; }
			set { _buttonCssClass = value; }
		}

		/// <summary>
		/// This is the primary key value of the content child item you are assocating. (A forum, where content type = thread, we would set PostID here because a post is a child of the thread).
		/// </summary>
		/// <remarks>This should only be set in combination with ChildType.</remarks>
		[Browsable(false)]
		public int ChildKeyID {
			get {
				var childKeyID = 0;
				if (ViewState["ChildKeyID"] != null) {
					childKeyID = Convert.ToInt32(ViewState["ChildKeyID"]);
				}
				return childKeyID;
			}
			set { ViewState["ChildKeyID"] = value; }
		}

		/// <summary>
		/// The name value for the child type to associate the comment with. This would be a level below the content type level. (ie. a 'forum post' would be a child type, since it is one level below the content type 'forum thread'). 
		/// </summary>
		/// <remarks>This should only be set in combination with ChildKeyID.</remarks>
		[Browsable(false)]
		public string ChildType {
			get {
				var childType = "";
				if (ViewState["ChildType"] != null) {
					childType = Convert.ToString(ViewState["ChildType"]);
				}
				return childType;
			}
			set { ViewState["ChildType"] = value; }
		}

		/// <summary>
		/// Determines if the current user has their comments moderated.
		/// </summary>
		[Bindable(false), Browsable(true), Category("Behavior"), DefaultValue("false"),
		 Description("Determines if the current user has their comments moderated."), Localizable(false)]
		public bool CurrentUserIsApproved {
			get {
				var isApproved = CurrentUserIsModerator;
				if (ViewState["CurrentUserIsApproved"] != null) {
					isApproved = Convert.ToBoolean(ViewState["CurrentUserIsApproved"]);
				}
				return isApproved;
			}
			set { ViewState["CurrentUserIsApproved"] = value; }
		}

		/// <summary>
		/// Determines if the current user can moderate comments. This should be set to true for admin, page editors, moderators (based on security set in your consuming module). 
		/// </summary>
		[Bindable(false), Browsable(true), Category("Behavior"), DefaultValue("false"), 
		 Description("Determines if the current user is permitted to approve/edit/delete comments."), Localizable(false)]
		public bool CurrentUserIsModerator
		{
			get {
				if (CurrentUserID > 0)
				{
					var isModerator = false;
					if (ViewState["CurrentUserIsModerator"] != null) {
						isModerator = Convert.ToBoolean(ViewState["CurrentUserIsModerator"]);
					}
					return isModerator;
				}
				return false;
			}
			set { ViewState["CurrentUserIsModerator"] = value; }
		}

		/// <summary>
		/// Determines if the user will be permitted to enter a review title, if not it will be left blank/null. 
		/// </summary>
		[Bindable(true), Browsable(true), Category("Behavior"), DefaultValue("false"), 
		Description("Determines if the review title field should be displayed for adding a review."), Localizable(false)]
		public bool EnableReviewTitle { get; set; }

		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("Bottom"),
		Description("Where the pageer should be displayed in relation to the review list."), Localizable(false)]
		public ControlBehavior.FormPlacementMode InputFormPlacement { get; set; }

		/// <summary>
		/// This is the Css class applied to the labels next to the textboxes (for title, author, email, website, comment).
		/// </summary>
		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("SubHead"), 
		Description("The Css class applied to all labels in the control."), Localizable(false)]
		public string LabelCssClass {
			get { return _labelCssClass; }
			set { _labelCssClass = value; }
		}

		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("TopAndBottom"),
		Description("Where the pageer should be displayed in relation to the review list."), Localizable(false)]
		public ControlBehavior.PagerPlacementMode PagerPlacement { get; set; }

		/// <summary>
		/// The number of items to display at once in the data grid.
		/// </summary>
		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("5"), 
		Description("The number of items to display at once in the data grid."), Localizable(false)]
		public int PageSize {
			get {
				if (ViewState["PageSize"] != null) {
					_pageSize = Convert.ToInt32(ViewState["PageSize"]);
				}
				return _pageSize;
			}
			set { ViewState["PageSize"] = value; }
		}

		/// <summary>
		/// Determines if review require authentication for submission.
		/// </summary>
		/// <remarks>This will eventually be public and settable.</remarks>
		[Browsable(false), Localizable(false)]
		private bool RequireAuthentication {
			get { return _requireAuthentication; }
			//set { _requireAuthentication = value; }
		}

		/// <summary>
		/// 
		/// </summary>
		[Browsable(false), Localizable(false)]
		private bool AllowUserMultipleReviews {
			get { return _allowUserMultipleReviews; }
			//set { _allowUserMultipleReviews = value; }
		}

		/// <summary>
		/// The Css class applied to all textboxes, input or textarea, in the control. 
		/// </summary>
		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("NormalTextBox"),
		Description("The Css class applied to all textboxes, input or textarea,  in the control."), Localizable(false)]
		public string TextBoxCssClass
		{
			get { return _textBoxCssClass; }
			set { _textBoxCssClass = value; }
		}

		/// <summary>
		/// This is the Css class applied to any validators on the page.
		/// </summary>
		[Bindable(true), Browsable(true), Category("Appearance"), DefaultValue("NormalRed"), 
		Description("The Css class applied to all validators in the control."), Localizable(false)]
		public string ValidatorCssClass {
			get { return _validatorCssClass; }
			set { _validatorCssClass = value; }
		}

		public ITemplate MyLayoutTemplate
		{
			get
			{
				// Load html file
				var layoutTemplate = Page.LoadTemplate("~/DesktopModules/ContentLibrary/Components/WebControls/Templates/ReviewLayout.ascx");

				return layoutTemplate;
			}
		}

		#endregion

		#region Event Handlers

		/// <summary>
		/// 
		/// </summary>
		protected override void RecreateChildControls() {
			EnsureChildControls();
		}

		/// <summary>
		/// Adds the necessary control(s) to the controls collection.
		/// </summary>
		protected override void CreateChildControls() {
			base.CreateChildControls();
			Controls.Clear();

			var sm = ScriptManager.GetCurrent(Page);

			_listView = new DnnListView
			            	{
			            		Width = Width,
			            		PageSize = PageSize,
			            		AllowPaging = true,
			            		AllowCustomPaging = true,
			            		ID = String.Concat(ID, "_ListView"),
                                LayoutTemplate = new ReviewLayoutTemplate(PageSize, Width, PagerPlacement),
                            //LayoutTemplate = MyLayoutTemplate,
				ItemTemplate = new Review(ListItemType.Item, CurrentUserID, PortalID, CurrentUserIsModerator, SharedResourceFile),
				AlternatingItemTemplate = new Review(ListItemType.AlternatingItem, CurrentUserID, PortalID, CurrentUserIsModerator, SharedResourceFile),
				DataKeyNames = new[] { "ReviewID" }
			};
			_listView.NeedDataSource += NeedDataSource;
			_listView.PageIndexChanged += PageIndexChanged;
			_listView.ItemCommand += ItemCommand;

			Controls.Add(_listView); // NeedDataSource will fire immediately after this line. 

			_title = new TextBox { ID = String.Concat(ID, "_TitleTxt"), MaxLength = 255, CssClass = TextBoxCssClass };
			_overallRating = new DnnRating { ID = String.Concat(ID, "_OverallRating") };
			_proComment = new TextBox { ID = String.Concat(ID, "_ProCommentTxt"), TextMode = TextBoxMode.MultiLine, CssClass = TextBoxCssClass };
			_conComment = new TextBox { ID = String.Concat(ID, "_ConCommentTxt"), TextMode = TextBoxMode.MultiLine, CssClass = TextBoxCssClass };
			_comment = new TextBox { ID = String.Concat(ID, "_CommentTxt"), TextMode = TextBoxMode.MultiLine, CssClass = TextBoxCssClass };

			_valComment = new RequiredFieldValidator
			{
				ID = String.Concat(ID, "_ValComment"),
				CssClass = ValidatorCssClass,
				Text = Services.Localization.Localization.GetString("valReqComment.Validator", SharedResourceFile),
				ControlToValidate = _comment.UniqueID
			};

			_cmdSubmit = new LinkButton { ID = String.Concat(ID, "_SubmitBtn"), CssClass = ButtonCssClass, Text = Services.Localization.Localization.GetString("cmdSubmit", SharedResourceFile), CausesValidation = true };
			_cmdSubmit.Click += CmdSubmitClick;

			Controls.Add(_title);
			Controls.Add(_overallRating);
			Controls.Add(_proComment);
			Controls.Add(_conComment);
			Controls.Add(_comment);
			Controls.Add(_valComment);
			Controls.Add(_cmdSubmit);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e) {
			base.OnPreRender(e);

			// setup control props
			if (!EnableReviewTitle) {
				_title.Visible = false;
			}

			if (ReviewID > 0)
			{
				var controller = new ContentLibraryController();
				var objReview = controller.GetReview(ReviewID);

				if (objReview != null) {
					IsEditMode = true;
					_title.Text = objReview.Title;
                    _overallRating.Value = objReview.ReviewRatings.Value;
					_proComment.Text = objReview.ProComment;
					_conComment.Text = objReview.ConComment;
					_comment.Text = objReview.Comment;
				}
			}

			EnsureChildControls();
		}


		/// <summary>
		/// Creates the user interface.
		/// </summary>
		/// <param name="writer"></param>
		protected override void Render(HtmlTextWriter writer)
		{
			if (InputFormPlacement == ControlBehavior.FormPlacementMode.Top)
			{
				if (RequireAuthentication & CurrentUserID > 0) {
					writer = RenderReviewForm(writer);
				}

				writer.AddAttribute(HtmlTextWriterAttribute.Id, "reviews");
				writer.RenderBeginTag(HtmlTextWriterTag.Div);
				_listView.RenderControl(writer);
				writer.RenderEndTag();
			}
			else
			{
				writer.AddAttribute(HtmlTextWriterAttribute.Id, "reviews");
				writer.RenderBeginTag(HtmlTextWriterTag.Div);
				_listView.RenderControl(writer);
				writer.RenderEndTag();

				if (RequireAuthentication & CurrentUserID > 0) {
					RenderReviewForm(writer);
				}
			}
		}

		/// <summary>
		/// Populates the grid with data, when necessary.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void NeedDataSource(object sender, RadListViewNeedDataSourceEventArgs e) {
			var controller = new ContentLibraryController();
			var currentChildTypeID = ChildTypeID();
			var colReviews = controller.GetApprovedReviews(CurrentContentItemID, ChildKeyID, currentChildTypeID);

			_listView.DataSource = colReviews;
			_listView.VirtualItemCount = colReviews.Count > 0 ? colReviews[0].TotalRecords : 0;
		}

		/// <summary>
		/// Fires when the user changes the current page in the grid.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void PageIndexChanged(object sender, RadListViewPageChangedEventArgs e) {
			PageIndex = e.NewPageIndex;
			ResetFields();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void ItemCommand(object sender, RadListViewCommandEventArgs e) {
			if (e.CommandName == "Select")
			{
				ReviewID = Convert.ToInt32(e.CommandArgument);
				IsEditMode = true;
			}
			if (e.CommandName == "DeleteItem")
			{
				ReviewID = Convert.ToInt32(e.CommandArgument);
				// add logic to delete from db.

				ReviewID = 0;
				IsEditMode = false;
				_listView.Rebind();
			}

			if (e.CommandName != "Approve") return;
			ReviewID = Convert.ToInt32(e.CommandArgument);
			// add logic to approve in db.

			ReviewID = 0;
			IsEditMode = false;
			_listView.Rebind();
		}

		/// <summary>
		/// The Submit event.
		/// </summary>
		/// <remarks>This is normally done behind the scenes by .net, implemented here for performance reasons.</remarks>
		[Category("Action"),
		Description("Raised when the user clicks the submit button.")]
		public event EventHandler Submit {
			add {
				Events.AddHandler(EventSubmitKey, value);
			}
			remove {
				Events.RemoveHandler(EventSubmitKey, value);
			}
		}

		/// <summary>
		/// The method that raises the Submit event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnSubmit(EventArgs e) {
			var submitHandler = (EventHandler)Events[EventSubmitKey];
			if (submitHandler != null) {
				submitHandler(this, e);
			}

			SaveReview();
		}

		/// <summary>
		/// Handles the Click event of the Button and raises the Submit event.
		/// </summary>
		/// <param name="source"></param>
		/// <param name="e"></param>
		private void CmdSubmitClick(object source, EventArgs e) {
			OnSubmit(EventArgs.Empty);
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Saves a comment to the data store, once it is validated as valid.
		/// </summary>
		private void SaveReview() {
			var controller = new ContentLibraryController();
			var objReview = new ReviewInfo
			{
				ContentItemID = CurrentContentItemID,
				ChildKeyID = ChildKeyID,
				ChildTypeID = ChildTypeID()
			};

			// we need to sanitize all input
			if (EnableReviewTitle) {
				objReview.Title = new Security.PortalSecurity().InputFilter(_title.Text,
														Security.PortalSecurity.FilterFlag.NoMarkup |
														Security.PortalSecurity.FilterFlag.NoScripting);
			}

			objReview.ProComment = new Security.PortalSecurity().InputFilter(_proComment.Text,
														Security.PortalSecurity.FilterFlag.NoMarkup |
														Security.PortalSecurity.FilterFlag.NoScripting);
			objReview.ConComment = new Security.PortalSecurity().InputFilter(_conComment.Text,
														Security.PortalSecurity.FilterFlag.NoMarkup |
														Security.PortalSecurity.FilterFlag.NoScripting);
			objReview.Comment = new Security.PortalSecurity().InputFilter(_comment.Text,
														Security.PortalSecurity.FilterFlag.NoMarkup |
														Security.PortalSecurity.FilterFlag.NoScripting);

			// not supported yet, always set to false.
			objReview.Indexed = false;

			if (IsEditMode) {
				objReview.LastModifiedByUserID = CurrentUserID;
				objReview.LastModifiedOnDate = DateTime.Now;
				objReview.ReviewID = ReviewID;

				if (CurrentUserIsModerator || CurrentUserIsApproved) {
					objReview.Approved = true;
					objReview.ApprovedByUserID = CurrentUserID;
					objReview.ApprovedOnDate = DateTime.Now;
				} else
				{
					objReview.Approved = false;
				}

				controller.UpdateReview(objReview);

				// handle ratings
				var objReviewRate = new ReviewRatingInfo
				{
					ReviewID = ReviewID,
					Value = _overallRating.Value,
					LastModifiedOnDate = DateTime.Now
				};

				controller.UpdateReviewRating(objReviewRate);
			} else {
				objReview.CreatedByUserID = CurrentUserID;
				objReview.CreatedOnDate = DateTime.Now;

				if (CurrentUserIsModerator || CurrentUserIsApproved) {
					objReview.Approved = true;
					objReview.ApprovedByUserID = CurrentUserID;
					objReview.ApprovedOnDate = DateTime.Now;
				} else {
					objReview.Approved = false;
				}

				var reviewID = controller.AddReview(objReview);

				// handle ratings
				var objReviewRate = new ReviewRatingInfo
				{
					ReviewID = reviewID,
					Value = _overallRating.Value,
					CreatedOnDate = DateTime.Now
				};

				controller.AddReviewRating(objReviewRate);
			}

			// final pieces
			IsEditMode = false;
			ResetFields();
			PageIndex = 0;
			_listView.CurrentPageIndex = 0;

			_listView.Rebind();
		}

		/// <summary>
		/// This creates the user interface for the comment form.
		/// </summary>
		/// <param name="writer"></param>
		/// <returns></returns>
		private HtmlTextWriter RenderReviewForm(HtmlTextWriter writer) {
			// <div>
			writer.AddAttribute(HtmlTextWriterAttribute.Id, "reviewform");
			writer.RenderBeginTag(HtmlTextWriterTag.Div);
			// <fieldset>
			writer.RenderBeginTag(HtmlTextWriterTag.Fieldset);
			// <legend>
			writer.RenderBeginTag(HtmlTextWriterTag.Legend);
			writer.Write(Services.Localization.Localization.GetString("ContributeReview", SharedResourceFile));
			writer.RenderEndTag();
			// <dl>
			writer.RenderBeginTag(HtmlTextWriterTag.Dl);

			if (EnableReviewTitle) {
				// <dt>
				writer.RenderBeginTag(HtmlTextWriterTag.Dt);
				// <label>
				writer.AddAttribute(HtmlTextWriterAttribute.Name, "lblTitle");
				writer.AddAttribute(HtmlTextWriterAttribute.Class, LabelCssClass);
				writer.RenderBeginTag(HtmlTextWriterTag.Label);
				writer.Write(Services.Localization.Localization.GetString("lblTitle", SharedResourceFile));
				// </label>
				writer.RenderEndTag();
				// </dt>
				writer.RenderEndTag();
				// <dd>
				writer.RenderBeginTag(HtmlTextWriterTag.Dd);
				_title.RenderControl(writer);
				// </dd>
				writer.RenderEndTag();
			}

			// Ratings
			// <dt>
			writer.RenderBeginTag(HtmlTextWriterTag.Dt);
			// <label>
			writer.AddAttribute(HtmlTextWriterAttribute.Name, "lblOverallRating");
			writer.AddAttribute(HtmlTextWriterAttribute.Class, LabelCssClass);
			writer.RenderBeginTag(HtmlTextWriterTag.Label);
			writer.Write(Services.Localization.Localization.GetString("lblOverallRating", SharedResourceFile));
			// </label>
			writer.RenderEndTag();
			// </dt>
			writer.RenderEndTag();
			// <dd>
			
			writer.RenderBeginTag(HtmlTextWriterTag.Dd);
			_overallRating.RenderControl(writer); // overall rating
			// </dd>
			writer.RenderEndTag();
			// End Ratings

			// Pros
			// <dt>
			writer.RenderBeginTag(HtmlTextWriterTag.Dt);
			// <label>
			writer.AddAttribute(HtmlTextWriterAttribute.Name, "lblPros");
			writer.AddAttribute(HtmlTextWriterAttribute.Class, LabelCssClass);
			writer.RenderBeginTag(HtmlTextWriterTag.Label);
			writer.Write(Services.Localization.Localization.GetString("lblPros", SharedResourceFile));
			// </label>
			writer.RenderEndTag();
			// </dt>
			writer.RenderEndTag();
			// <dd>
			writer.AddAttribute(HtmlTextWriterAttribute.Id, "reviewpros");
			writer.RenderBeginTag(HtmlTextWriterTag.Dd);
			_proComment.RenderControl(writer); // pros textbox
			// </dd>
			writer.RenderEndTag();
			// End Pros

			// Cons
			// <dt>
			writer.RenderBeginTag(HtmlTextWriterTag.Dt);
			// <label>
			writer.AddAttribute(HtmlTextWriterAttribute.Name, "lblCons");
			writer.AddAttribute(HtmlTextWriterAttribute.Class, LabelCssClass);
			writer.RenderBeginTag(HtmlTextWriterTag.Label);
			writer.Write(Services.Localization.Localization.GetString("lblCons", SharedResourceFile));
			// </label>
			writer.RenderEndTag();
			// </dt>
			writer.RenderEndTag();
			// <dd>
			writer.AddAttribute(HtmlTextWriterAttribute.Id, "reviewcons");
			writer.RenderBeginTag(HtmlTextWriterTag.Dd);
			_conComment.RenderControl(writer); // cons textbox
			// </dd>
			writer.RenderEndTag();
			// End Cons

			// Comment
			// <dt>
			writer.RenderBeginTag(HtmlTextWriterTag.Dt);
			// <label>
			writer.AddAttribute(HtmlTextWriterAttribute.Name, "lblOtherComment");
			writer.AddAttribute(HtmlTextWriterAttribute.Class, LabelCssClass);
			writer.RenderBeginTag(HtmlTextWriterTag.Label);
			writer.Write(Services.Localization.Localization.GetString("lblOtherComment", SharedResourceFile));
			// </label>
			writer.RenderEndTag();
			// </dt>
			writer.RenderEndTag();
			// <dd>
			writer.AddAttribute(HtmlTextWriterAttribute.Id, "reviewinput");
			writer.RenderBeginTag(HtmlTextWriterTag.Dd);
			_comment.RenderControl(writer); // other thoughts textbox
			// </dd>
			writer.RenderEndTag();
			// End Comment

			// Buttons
			// <dt>
			writer.RenderBeginTag(HtmlTextWriterTag.Dt);
			// <br>
			writer.RenderBeginTag(HtmlTextWriterTag.Br);
			// </br>
			writer.RenderEndTag();
			// </dt>
			writer.RenderEndTag();
			// <dd>
			writer.RenderBeginTag(HtmlTextWriterTag.Dd);
			// <buttons>
			// now we have to check if the user has submitted a review before here, we want to hide the submit button unless in edit mode (if they submitted one)
			if (AllowUserMultipleReviews || ReviewID > 0)
			{
				_cmdSubmit.RenderControl(writer); // save button
			}
			else
			{
				if (CurrentUserID > 0)
				{
					var controller = new ContentLibraryController();
					var colUserReviews = (from t in controller.GetUserReviews(CurrentUserID, PortalID) where t.ContentItemID == CurrentContentItemID & t.ChildKeyID == ChildKeyID & t.ChildTypeID == ChildTypeID()  select t);
					if (colUserReviews.Count() <  1)
					{
						_cmdSubmit.RenderControl(writer); // save button
					}			
				}
				else
				{
					_cmdSubmit.RenderControl(writer); // save button
				}
			}

			////if (IsEditMode) {
			////     writer.Write("&nbsp;&nbsp;");
			////     _delete.RenderControl(writer); // delete button
			////}

			// </dd>
			writer.RenderEndTag();
			// End Buttons
			// </dl>
			writer.RenderEndTag();
			// </fieldset>
			writer.RenderEndTag();
			// </div>
			writer.RenderEndTag();

			return writer;
		}

		/// <summary>
		/// This clears the review input form fields and forces the grid to rebind.
		/// </summary>
		private void ResetFields() {
			_title.Text = "";
			_overallRating.Value = 0;
			_proComment.Text = "";
			_conComment.Text = "";
			_comment.Text = "";
			ReviewID = 0;
		}

		#endregion

	}

}